package dbCore

import (
	"encoding/json"
	"mytinydb/config"
	"mytinydb/dataStructure"
	"mytinydb/utils"
	"os"
	"sync"
)

const (

	//保存配置的文件名称
	configSaveFile = string(os.PathSeparator) + "dbCore.cfg"

	//保存数据库相关信息的文件名称
	dbMetaSaveFile = string(os.PathSeparator) + "dbCore.meta"

	//回收磁盘空间时的临时目录
	reclaimPath = string(os.PathSeparator) + "rosedb_reclaim"

	//保存过期字典的文件名称
	expireFile = string(os.PathSeparator) + "dbCore.expires"

	//额外信息的分隔符，用于存储一些额外的信息（因此一些操作的value中不能包含此分隔符）
	ExtraSeparator = "\\0"
)

type ArchivedFile map[uint32]*DBfile

type Db struct {
	activateFile   *DBfile                 // 当前活跃文件
	activateFileId uint32                  // 当前活跃文件ID
	archFiles      ArchivedFile            // 已封存文件
	strIdxList     *dataStructure.SkipList // 字符串索引列表
	config         config.DbConfig
	mu             sync.RWMutex //mutex
}



// 构建字符串索引
func (db *Db) buildStringIndex(idx *Index, optype uint16) {
	if db.strIdxList == nil || idx == nil {
		return
	}
	// TODO 设置到期时间
	//now := uint32(time.Now().Unix())

	switch optype {
	case StringSet:
		db.strIdxList.Put(idx.Meta.Key, idx)
	}
}

func Open(config config.DbConfig) (*Db, error) {
	// 如果数据库数据文件不存在则创建
	if !utils.Exist(config.DirPath) {
		if err := os.MkdirAll(config.DirPath, os.ModePerm); err != nil {
			return nil, err
		}
	}

	//加载数据文件
	archFiles, activeFileId, err := Build(config.DirPath, config.FileRWMethod, config.BlockSize)
	if err != nil {
		return nil, err
	}

	activeFile, err := NewDBFile(config.DirPath, activeFileId, config.FileRWMethod, config.BlockSize)
	if err != nil {
		return nil, err
	}

	db := &Db{
		activateFile:   activeFile,
		activateFileId: activeFileId,
		archFiles:      archFiles,
		config:         config,
		strIdxList:     dataStructure.NewSkipList(),
	}
	return db, err
}

// Close 关闭数据库，保存相关配置
func (db *Db) Close() error {
	db.mu.Lock()
	defer db.mu.Unlock()

	if err := db.saveConfig(); err != nil {
		return err
	}
	//if err := dbCore.saveMeta(); err != nil {
	//	return err
	//}
	//if err := dbCore.expires.SaveExpires(dbCore.config.DirPath + expireFile); err != nil {
	//	return err
	//}
	if err := db.activateFile.Close(true); err != nil {
		return err
	}
	return nil
}

//关闭数据库之前保存配置
func (db *Db) saveConfig() (err error) {
	//保存配置
	path := db.config.DirPath + configSaveFile
	file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0600)

	bytes, err := json.Marshal(db.config)
	_, err = file.Write(bytes)
	err = file.Close()

	return
}

// 将entry写入到数据库中，其中包含具体的将entry写入到文件中
func (db *Db) store(entry *Entry) error {
	// 若写入的entry的大小超过一个文件的大小，则关闭该文件，打开一个新的文件
	if db.activateFile.Offset+int64(entry.Size()) > db.config.BlockSize {
		//TODO
	}
	// 写入数据
	if err := db.activateFile.Write(entry); err != nil {
		return err
	}

	// 数据持久化
	if db.config.Sync {
		if err := db.activateFile.Sync(); err != nil {
			return err
		}
	}

	return nil
}

// 构建索引
func (db *Db) buildIndex(entry *Entry, idx *Index) error {
	// 判断模式  0:键和值均存于内存中 1:只有键存于内存中
	if db.config.IdxMode == 0 {
		idx.Meta.Value = entry.Meta.Value
		idx.Meta.ValueSize = entry.Meta.ValueSize
	}
	switch entry.TypeOfData {
	case String:
		db.buildStringIndex(idx, entry.OpType)
	}
	return nil
}
